package org.jsola.hr.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsola.admin.entity.SiteDO;
import org.jsola.common.FileKit;
import org.jsola.core.Page;
import org.jsola.core.entity.BaseDO;
import org.jsola.exception.ParamException;
import org.jsola.file.common.Zip4jKit;
import org.jsola.file.constant.RepoAuthority;
import org.jsola.file.entity.FileDO;
import org.jsola.file.entity.FileVersionDO;
import org.jsola.file.entity.RepoDO;
import org.jsola.file.exception.FileException;
import org.jsola.file.service.IFileService;
import org.jsola.file.service.IQueryFileService;
import org.jsola.file.service.IRepoService;
import org.jsola.hr.dao.IEmpInfoDAO;
import org.jsola.hr.dto.HrEmpInfoDownloadDTO;
import org.jsola.hr.entity.CompanyDO;
import org.jsola.hr.entity.EmpInfoDO;
import org.jsola.hr.provider.IAdminProviderService;
import org.jsola.hr.query.EmpInfoQuery;
import org.jsola.hr.service.ICompanyService;
import org.jsola.hr.service.IEmpInfoExtraService;
import org.jsola.hr.vo.EmpInfoPasteVO;
import org.jsola.hr.vo.EmpInfoVO;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import tk.mybatis.mapper.entity.Example;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;


/**
 * 员工信息
 *
 * @author zhr
 */
@Service("hrEmpInfoExtraServiceImpl")
@Slf4j
public class EmpInfoExtraServiceImpl implements IEmpInfoExtraService {

    @Autowired
    private IEmpInfoDAO empInfoDAO;

    @Autowired
    private IAdminProviderService adminProviderService;

    @Autowired
    private IQueryFileService queryFileService;

    @Autowired
    private IRepoService repoService;

    @Autowired
    private IFileService fileService;

    @Autowired
    private ICompanyService companyService;

    @Override
    public Page<EmpInfoVO> selectSiteEmp(EmpInfoQuery empInfoQuery, TokenUser tokenUser) {
        List<String> companyIds = companyService.tileCompanyIdByUser(tokenUser);
        if (CollectionUtils.isEmpty(companyIds)) {
            return new Page<>(empInfoQuery.getPageSize(), empInfoQuery.getPageNo());
        }
        Example example = new Example(EmpInfoDO.class);
        example.and()
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andEqualTo("valid", true);
        if (!StringUtils.isEmpty(empInfoQuery.getNameOrPhoneOrIdCard())) {
            example.and()
                    .orLike("name", "%" + empInfoQuery.getNameOrPhoneOrIdCard() + "%")
                    .orLike("idCard", "%" + empInfoQuery.getNameOrPhoneOrIdCard() + "%")
                    .orLike("phone", "%" + empInfoQuery.getNameOrPhoneOrIdCard() + "%");
        }
        if (!StringUtils.isEmpty(empInfoQuery.getCompanyId())) {
            example.and().andEqualTo("companyId", empInfoQuery.getCompanyId());
        } else {
            example.and().andIn("companyId", companyIds);
        }
        Page<EmpInfoDO> empInfoPage = empInfoDAO
                .selectPageByExample(example, empInfoQuery.getPageNo(), empInfoQuery.getPageSize());
        Page<EmpInfoVO> returnPage = empInfoPage.to(EmpInfoVO.class);
        List<EmpInfoVO> returnList = returnPage.getEntities();
        if (!CollectionUtils.isEmpty(returnList)) {
            List<String> companyIdList = returnList.stream()
                    .map(EmpInfoVO::getCompanyId).distinct().collect(Collectors.toList());
            List<CompanyDO> companyList = companyService.selectByIds(companyIdList, tokenUser.getSiteId());
            Map<String, CompanyDO> companyMap = companyList.stream().collect(Collectors.toMap(CompanyDO::getId, v -> v));
            for (EmpInfoVO empInfo : returnList) {
                CompanyDO company = companyMap.get(empInfo.getCompanyId());
                if (!ObjectUtils.isEmpty(company)) {
                    empInfo.setCompanyName(company.getName());
                }
            }
        }
        return returnPage;
    }

    @Override
    public EmpInfoPasteVO selectSiteEmpStickupSelect(List<String> idCardList, TokenUser tokenUser) {
        ParamException.notEmpty(idCardList, "身份证号不能为空");
        List<String> companyIds = companyService.tileCompanyIdByUser(tokenUser);
        EmpInfoPasteVO result = new EmpInfoPasteVO();
        if (CollectionUtils.isEmpty(companyIds)) {
            return result;
        }
        Example example = new Example(EmpInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andIn("idCard", idCardList)
                .andIn("companyId", companyIds);
        List<EmpInfoDO> empInfoList = empInfoDAO.selectByExample(example);
        if (CollectionUtils.isEmpty(empInfoList)) {
            result.setIdCardList(idCardList);
            return result;
        }
        List<EmpInfoVO> empInfoVOList = empInfoList.stream()
                .map(s -> s.to(EmpInfoVO.class)).collect(Collectors.toList());
        List<String> companyIdList = empInfoVOList.stream()
                .map(EmpInfoVO::getCompanyId).distinct().collect(Collectors.toList());
        List<CompanyDO> companyList = companyService.selectByIds(companyIdList, tokenUser.getSiteId());
        result.setEmpInfoVOList(empInfoVOList);
        Map<String, CompanyDO> companyMap = companyList.stream().collect(Collectors.toMap(CompanyDO::getId, v -> v));
        // 去除已查询出的身份证
        idCardList.removeAll(empInfoVOList.parallelStream().map(EmpInfoVO::getIdCard)
                .filter(Objects::nonNull).distinct().collect(Collectors.toList()));
        for (EmpInfoVO empInfo : empInfoVOList) {
            CompanyDO company = companyMap.get(empInfo.getCompanyId());
            if (!ObjectUtils.isEmpty(company)) {
                empInfo.setCompanyName(company.getName());
            }
        }
        result.setIdCardList(idCardList);
        return result;
    }

    @Override
    public String downIdCard(HrEmpInfoDownloadDTO hrEmpInfoDownloadDTO, TokenUser tokenUser) {
        Example example = new Example(EmpInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andIn("companyId", hrEmpInfoDownloadDTO.getCompanyIdList());
        // 员工状态
        if (!CollectionUtils.isEmpty(hrEmpInfoDownloadDTO.getStatusList())) {
            example.and().andIn("status", hrEmpInfoDownloadDTO.getStatusList());
        }
        // 仅查询如下字段！！！！！！！！
        example.selectProperties("companyId", "name", "idCard", "annexIdCard");
        List<EmpInfoDO> empInfoList = empInfoDAO.selectByExample(example);
        ParamException.isFalse(CollectionUtils.isEmpty(empInfoList), "没有需要导出的员工");
        // 文件路径-文件名称(姓名-身份证-x)
        Map<String, String> pathNameMap = new HashMap<>(16);
        // 文件路径-用工单位名称
        Map<String, String> pathCompanyNameMap = new HashMap<>(16);

        List<String> companyIds = empInfoList.parallelStream().map(EmpInfoDO::getCompanyId)
                .distinct().collect(Collectors.toList());
        List<CompanyDO> companyDOList = companyService.selectByIds(companyIds, tokenUser.getSiteId());
        // 用工单位Id-名称
        Map<String, String> companyIdNameMap = companyDOList
                .parallelStream().collect(Collectors.toMap(BaseDO::getId, CompanyDO::getName));
        // 每个员工的姓名
        for (EmpInfoDO empInfoDO : empInfoList) {
            String annexIdCard = empInfoDO.getAnnexIdCard();
            if (StringUtils.isEmpty(annexIdCard)) {
                continue;
            }
            String[] split = annexIdCard.split(",");
            for (int i = 0; i < split.length; i++) {
                pathNameMap.put(split[i], empInfoDO.getName() + "-" + empInfoDO.getIdCard() + "-" + (i + 1));
                pathCompanyNameMap.put(split[i], companyIdNameMap.get(empInfoDO.getCompanyId()));
            }
        }
        // 文件Id-文件名
        Map<String, String> fileIdNameMap = new HashMap<>(16);
        Map<String, String> fileIdCompanyNameMap = new HashMap<>(16);
        // 查询文件路径
        Set<String> pathList = pathNameMap.keySet();
        for (String path : pathList) {
            String fileId = getFileIdByUrl(path);
            fileIdNameMap.put(fileId, pathNameMap.get(path));
            fileIdCompanyNameMap.put(fileId, pathCompanyNameMap.get(path));
        }
        // 文件Id-查询文件路径
        List<FileDO> fileUrlList = queryFileService.listAll(tokenUser.getSiteId()
                , fileIdNameMap.keySet().toArray(new String[]{}));
        // 查询所有下载的文件
        ParamException.notEmpty(fileUrlList, "文件不存在或都已被删除");
        if (pathList.size() != fileUrlList.size()) {
            log.error("有文件不存在或被删除了, 查询文件路径：{}, 下载文件路径：{}", pathList, fileUrlList);
            throw new ParamException("有文件不存在或被删除了");
        }
        // 文件名为租户名
        Map<String, SiteDO> siteMap = adminProviderService.selectSiteMap();
        SiteDO siteDO = siteMap.get(tokenUser.getSiteId());
        ParamException.notNull(siteDO, "租户信息不存在");
        String zipName = siteDO.getName();
        // 有压缩文件名称的,根据压缩文件名称生成临时目录,没有的根据uuid生成
        String allFileDir = getSystemTempPathByZipName(zipName);
        // 资料库
        RepoDO repoDO = repoService.findDefaultSiteRepo(RepoAuthority.REPO_ACL_RW.getValue(), Boolean.FALSE, tokenUser.getSiteId());
        String repoId = repoDO.getId();
        FileException.notNull(repoDO, "没有找到默认的站点资料库，请联系管理员");

        // 先清空历史数据
        String zipFileName = "file-" + System.currentTimeMillis() + ".zip";
        if (StringUtils.isNotEmpty(zipName)) {
            zipFileName = zipName + ".zip";
        }
        String outFile = getSystemTempPathByZipName(zipName) + zipFileName;
        // 生成压缩包
        deleteFile(new File(outFile));
        // 保存文件夹
        File dirFile = new File(allFileDir);
        File[] files = dirFile.listFiles();
        if (files != null) {
            for (File file : files) {
                deleteDirectory(file);
            }
        }
        fileUrlList.forEach(fileDO -> {
            if (fileDO.getDirectory()) {
                // 文件夹
                // 文件夹重命名问题
                String finalFileName = getFinalName(allFileDir, fileDO.getName());
                fileDO.setName(finalFileName);
                String folderPath = downloadFolder(fileDO, repoId, tokenUser);
                try {
                    FileUtils.moveDirectory(new File(folderPath), new File(allFileDir));
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            } else {
                // 文件
                FileVersionDO fileVersionDO = downloadSingleFile(fileDO.getFileUniId(), repoId, tokenUser);
                try {
                    // 文件Id
                    String fileId = fileVersionDO.getFileId();
                    // 文件重命名问题
                    String finalFileName = getFinalName(allFileDir, fileIdNameMap.get(fileId) + "." + fileDO.getExt());
                    FileUtils.copyFile(new File(fileVersionDO.getTempFilePath())
                            , new File(allFileDir + File.separator + fileIdCompanyNameMap.get(fileId)
                                    + File.separator + finalFileName));
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        // 压缩文件
        Zip4jKit.compressFolder(outFile, allFileDir);
        return outFile;
    }

    @Override
    public List<EmpInfoDO> selectByNamesAndSiteId(List<String> names, String siteId) {

        if (CollectionUtils.isEmpty(names)) {
            return new ArrayList<>();
        }
        Example example = new Example(EmpInfoDO.class);
        example.and()
                .andEqualTo("siteId", siteId)
                .andEqualTo("valid", true)
                .andIn("name", names);
        return empInfoDAO.selectByExample(example);
    }

    /**
     * 删除文件以及下级文件
     *
     * @param dirFile 文件
     */
    private void deleteDirectory(File dirFile) {
        // 如果dir对应的文件不存在，或者不是一个目录，则退出
        if (!dirFile.exists() || !dirFile.isDirectory()) {
            return;
        }
        // 删除文件夹下的所有文件(包括子目录)
        File[] files = dirFile.listFiles();
        if (files == null || files.length == 0) {
            return;
        }
        for (File file : files) {
            if (file.isFile()) {
                // 删除子文件
                deleteFile(file);
            } else {
                // 删除子目录
                deleteDirectory(file);
            }
        }
        // 删除当前目录
        ParamException.isTrue(dirFile.delete(), "删除目录" + dirFile.getAbsolutePath() + "失败！");
    }

    /**
     * 删除单个文件
     *
     * @param file 被删除文件的文件名
     */
    public static void deleteFile(File file) {
        if (!file.exists()) {
            return;
        }
        ParamException.isTrue(file.isFile(), file.getName() + "删除失败！它可能不是一个文件");
        boolean delete = file.delete();
        ParamException.isTrue(delete, "删除单个文件" + file.getName() + "失败！");
    }

    /**
     * 下载文件夹下所有文件到一个临时文件夹里，递归所有文件和文件夹
     */
    private String downloadFolder(FileDO folderFileDO, String repoId, TokenUser tokenUser) {
        String tempDir = FileKit.getSystemTempPath();
        // 递归所有文件和文件夹
        List<FileDO> allFileList = queryFileService.listAll(tokenUser.getSiteId(), folderFileDO.getId());
        // 筛选出来文件的唯一id
        List<String> downLoadFileUniIds = allFileList.stream().filter(fileDO -> !fileDO.getDirectory()).map(FileDO::getFileUniId).collect(Collectors.toList());
        Map<String, FileVersionDO> fileMap = new HashMap<>();
        // 全是空文件夹，不需要下载，只需要创建空文件夹
        if (!CollectionUtils.isEmpty(downLoadFileUniIds)) {
            // 下载所有文件
            List<FileVersionDO> fileVersionList = fileService.download(downLoadFileUniIds, repoId, tokenUser);
            // 转成map
            fileMap = fileVersionList.parallelStream()
                    .collect(
                            Collectors.toMap(
                                    FileVersionDO::getFileUniId,
                                    fileVersionDO -> fileVersionDO)
                    );
        }
        // 递归复制文件夹下的所有文件
        copyChildFile(folderFileDO, tempDir, allFileList, fileMap);
        // 返回文件夹的路径
        String folderPath = tempDir + File.separator + folderFileDO.getName();
        FileException.isTrue(new File(folderPath).exists(), "下载文件夹失败，fileId:" + folderFileDO.getId());
        return folderPath;
    }


    /**
     * 获取目录下的重命名后的文件名，如果有重名
     *
     * @param allFileDir 目录
     * @param fileName   文件名
     * @return 重命名后的文件名
     */
    private String getFinalName(String allFileDir, String fileName) {
        File[] allFiles = new File(allFileDir).listFiles();
        if (allFiles == null || allFiles.length == 0) {
            return fileName;
        }
        return FileKit.getFinalFileName(
                Arrays.stream(allFiles).map(File::getName).collect(Collectors.toList()),
                fileName
        );
    }


    /**
     * 下载单个文件
     */
    private FileVersionDO downloadSingleFile(String fileUniId, String repoId, TokenUser tokenUser) {
        List<FileVersionDO> fileVersionList = fileService.download(Collections.singletonList(fileUniId), repoId, tokenUser);
        FileException.notEmpty(fileVersionList, "文件下载失败,没有下载到任何文件，fileUniId：" + fileUniId);
        FileVersionDO fileVersionDO = fileVersionList.get(0);
        FileException.notNull(fileVersionDO, "文件下载失败，下载到文件为null，fileUniId：" + fileUniId);
        FileException.isTrue(new File(fileVersionDO.getTempFilePath()).exists(), "文件下载失败，下载文件不存在，fileUniId：" + fileUniId);
        return fileVersionDO;
    }

    /**
     * 根据压缩文件名称生成系统临时目录路径
     *
     * @param zipName 压缩文件名称
     * @return 系统临时目录路径
     */
    private String getSystemTempPathByZipName(String zipName) {
        String tempFolder = System.getProperty("java.io.tmpdir");
        if (!tempFolder.endsWith(File.separator)) {
            tempFolder += File.separator;
        }

        // 有压缩文件名称的,根据压缩文件名称,没有的根据随机uuid
        if (StringUtils.isNotEmpty(zipName)) {
            tempFolder += zipName;
        } else {
            tempFolder += UUID.randomUUID().toString();
        }

        tempFolder += File.separator;
        return tempFolder;
    }

    /**
     * 从查询下载地址中获取文件Id(f_file_version表中file_id)
     *
     * @param siteFileUrl 查询下载地址
     * @return 文件Id
     */
    private String getFileIdByUrl(String siteFileUrl) {
        log.info("siteFileUrl:" + siteFileUrl);
        if (!siteFileUrl.contains("/")) {
            throw new ParamException("下载地址有误");
        }
        //https://abc.com/file/api/v1/site/view/inline/40659446784858880/40659446784858624/origin(15).pdf
        siteFileUrl = siteFileUrl.substring(0, siteFileUrl.lastIndexOf("/"));
        log.info("siteFileUrl:" + siteFileUrl);
        // 40659446784858624
        String fileId = siteFileUrl.substring(siteFileUrl.lastIndexOf("/") + 1);
        log.info("fileId:" + fileId);
        ParamException.hasText(fileId, "下载地址有误，没有获取到fileId");
        return fileId;
    }

    /**
     * 递归复制文件夹下的所有文件
     */
    private void copyChildFile(FileDO folderFile, String parentPath, List<FileDO> allFileList, Map<String, FileVersionDO> fileMap) {
        if (!folderFile.getDirectory()) {
            return;
        }
        String folderFilePath = parentPath + File.separator + folderFile.getName();
        boolean flg = new File(folderFilePath).mkdirs();
        List<FileDO> childList = allFileList.parallelStream().filter(fileDO -> fileDO.getParentId().equals(folderFile.getId())).collect(Collectors.toList());
        for (FileDO fileDO : childList) {
            if (fileDO.getDirectory()) {
                copyChildFile(fileDO, folderFilePath, allFileList, fileMap);
            } else {
                FileVersionDO fileVersionDO = fileMap.get(fileDO.getFileUniId());
                FileException.notNull(fileVersionDO, "文件不存在？fileUnionId:" + fileDO.getFileUniId());
                try {
                    FileUtils.copyFile(new File(fileVersionDO.getTempFilePath()), new File(parentPath + File.separator + fileVersionDO.getName()));
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

}





